package generator

import (
	"bytes"
	"fmt"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"gitee.com/zawei/yggdrasil/internal/yggdrasil/gen/fs"
	"gitee.com/zawei/yggdrasil/pkg/config"
	"gitee.com/zawei/yggdrasil/pkg/utils/xstrings"
	"github.com/dave/jennifer/jen"
)

// NewProject implements Gen and is used to create a new project.
type NewProject struct {
	BaseGenerator
	name          string
	interfaceName string
	projectPath   string
	filePath      string
}

// NewNewService returns a initialized and ready generator.
//
// The name parameter is the name of the service that will be created
// this name should be without the `Service` suffix
func NewNewProject(name string) Gen {
	gs := &NewProject{
		name:          name,
		interfaceName: xstrings.StrToCamelCase(config.Get("gen.new.project.service").String() + "Service"),
		projectPath:   filepath.Join(config.Get("gen.new.project.path").String(), xstrings.StrToLowerSnakeCase(name)),
	}
	gs.filePath = path.Join(gs.projectPath, "internal", "app", config.Get("gen.new.project.service").String(), "transport", config.Get("gen.new.project.service").String()+".go")
	gs.srcFile = jen.NewFilePath(strings.Replace(gs.projectPath, "\\", "/", -1))
	gs.InitPg()
	gs.fs = fs.Get()
	return gs
}

func (g *NewProject) Generate() error {
	if err := g.CreateFolderStructure(g.projectPath); err != nil {
		return err
	}
	if err := g.CreateFolderStructure(path.Join(g.projectPath, "cmd", config.Get("gen.new.project.service").String())); err != nil {
		return err
	}
	if err := g.CreateFolderStructure(path.Join(g.projectPath, "api", config.Get("gen.new.project.service").String())); err != nil {
		return err
	}
	if err := g.CreateFolderStructure(path.Join(g.projectPath, "internal", "app", config.Get("gen.new.project.service").String(), "transport")); err != nil {
		return err
	}
	err := g.genModule()
	if err != nil {
		return err
	}
	comments := []string{
		"Add your methods here",
		"e.x: Foo(ctx context.Context, s string) (rs string, err error)",
	}

	partial := NewPartialGenerator(nil)
	partial.appendMultilineComment(comments)
	g.code.Raw().Commentf("%s describes the service.", g.interfaceName).Line()
	g.code.appendInterface(g.interfaceName, []jen.Code{partial.Raw()})
	return g.fs.WriteFile(g.filePath, g.srcFile.GoString(), false)
}

func (g *NewProject) genModule() error {
	prjName := xstrings.StrToLowerSnakeCase(g.name)
	exist, _ := g.fs.Exists(prjName + "/go.mod")
	if exist {
		return nil
	}

	moduleName := prjName
	if config.Get("gen.new.project.module").String() != "" {
		moduleName = config.Get("gen.new.project.module").String()
		moduleNameSlice := strings.Split(moduleName, "/")
		moduleNameSlice[len(moduleNameSlice)-1] = xstrings.StrToLowerSnakeCase(moduleNameSlice[len(moduleNameSlice)-1])
		moduleName = strings.Join(moduleNameSlice, "/")
	}
	cmdStr := "cd " + prjName + " && go mod init " + moduleName
	cmd := exec.Command("sh", "-c", cmdStr)

	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	_, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("genModule: sh -c %s => err:%v", cmdStr, err.Error()+" , "+stderr.String())
	}
	return nil
}
